﻿// Inner Fire 游戏引擎库
// 场景分装 - 实例化与剔除
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-28
#include <dx_graphics.h>

namespace ifire {
void DxGraphics::Instanced_Update() {
  Instanced_UpdateInstanceData();
  Instanced_UpdateMaterialBuffer();
  UpdateMainPassCB();
}
void DxGraphics::Instanced_InitPipeline() {
  Instanced_LoadTextures();
  Instanced_BuildRootSignature();
  Instanced_BuildDescriptorHeaps();
  Instanced_BuildShadersAndInputLayout();
  Instanced_BuildSkullGeometry();
  Instanced_BuildMaterials();
  Instanced_BuildRenderItems();
  Instanced_BuildFrameResources();
  Instanced_BuildPSOs();
}
void DxGraphics::Instanced_Draw() {
  auto cmdListAlloc = current_frame_resource_->CmdListAlloc;

  // Reuse the memory associated with command recording.
  // We can only reset when the associated command lists have finished execution
  // on the GPU.
  ThrowIfFailed(cmdListAlloc->Reset());

  // A command list can be reset after it has been added to the command queue
  // via ExecuteCommandList. Reusing the command list reuses memory.
  ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));

  mCommandList->RSSetViewports(1, &mScreenViewport);
  mCommandList->RSSetScissorRects(1, &mScissorRect);

  // Indicate a state transition on the resource usage.
  auto to_render = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
  mCommandList->ResourceBarrier(1, &to_render);

  // Clear the back buffer and depth buffer.
  auto dsv = DepthStencilView();
  auto back = CurrentBackBufferView();
  mCommandList->ClearRenderTargetView(back, Colors::LightSteelBlue, 0, nullptr);
  mCommandList->ClearDepthStencilView(dsv,
      D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

  // Specify the buffers we are going to render to.
  mCommandList->OMSetRenderTargets(1, &back, true, &dsv);

  ID3D12DescriptorHeap* descriptorHeaps[] = {mSrvDescriptorHeap.Get()};
  mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

  mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

  // Bind all the materials used in this scene.  For structured buffers, we can
  // bypass the heap and set as a root descriptor.
  auto matBuffer = current_frame_resource_->MaterialBuffer->Resource();
  mCommandList->SetGraphicsRootShaderResourceView(
      1, matBuffer->GetGPUVirtualAddress());

  auto passCB = current_frame_resource_->PassCB->Resource();
  mCommandList->SetGraphicsRootConstantBufferView(
      2, passCB->GetGPUVirtualAddress());

  // Bind all the textures used in this scene.
  mCommandList->SetGraphicsRootDescriptorTable(
      3, mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());

  Instanced_DrawRenderItems(mCommandList.Get(), mOpaqueRitems);

  // Indicate a state transition on the resource usage.
  auto to_present = CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
      D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
  mCommandList->ResourceBarrier(1, &to_present);

  // Done recording commands.
  ThrowIfFailed(mCommandList->Close());

  // Add the command list to the queue for execution.
  ID3D12CommandList* cmdsLists[] = {mCommandList.Get()};
  mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

  // Swap the back and front buffers
  ThrowIfFailed(mSwapChain->Present(0, 0));
  mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

  // Advance the fence value to mark commands up to this fence point.
  current_frame_resource_->Fence = ++mCurrentFence;

  // Add an instruction to the command queue to set a new fence point.
  // Because we are on the GPU timeline, the new fence point won't be
  // set until the GPU finishes processing all the commands prior to this
  // Signal().
  mCommandQueue->Signal(mFence.Get(), mCurrentFence);
}

void DxGraphics::Instanced_LoadTextures() {

  auto bricksTex = std::make_unique<Texture>();
  bricksTex->Name = "bricksTex";
  bricksTex->Filename = "Textures/bricks.dds";
  ThrowIfFailed(LoadDDS(
      bricksTex->Filename.c_str(), bricksTex->Resource, bricksTex->UploadHeap));

  auto stoneTex = std::make_unique<Texture>();
  stoneTex->Name = "stoneTex";
  stoneTex->Filename = "Textures/stone.dds";
  ThrowIfFailed(LoadDDS(
      stoneTex->Filename.c_str(), stoneTex->Resource, stoneTex->UploadHeap));

  auto tileTex = std::make_unique<Texture>();
  tileTex->Name = "tileTex";
  tileTex->Filename = "Textures/tile.dds";
  ThrowIfFailed(LoadDDS(
      tileTex->Filename.c_str(), tileTex->Resource, tileTex->UploadHeap));

  auto crateTex = std::make_unique<Texture>();
  crateTex->Name = "crateTex";
  crateTex->Filename = "Textures/WoodCrate01.dds";
  ThrowIfFailed(LoadDDS(
      crateTex->Filename.c_str(), crateTex->Resource, crateTex->UploadHeap));

  auto iceTex = std::make_unique<Texture>();
  iceTex->Name = "iceTex";
  iceTex->Filename = "Textures/ice.dds";
  ThrowIfFailed(
      LoadDDS(iceTex->Filename.c_str(), iceTex->Resource, iceTex->UploadHeap));

  auto grassTex = std::make_unique<Texture>();
  grassTex->Name = "grassTex";
  grassTex->Filename = "Textures/grass.dds";
  ThrowIfFailed(LoadDDS(
      grassTex->Filename.c_str(), grassTex->Resource, grassTex->UploadHeap));

  auto defaultTex = std::make_unique<Texture>();
  defaultTex->Name = "defaultTex";
  defaultTex->Filename = "Textures/white1x1.dds";
  ThrowIfFailed(LoadDDS(defaultTex->Filename.c_str(), defaultTex->Resource,
      defaultTex->UploadHeap));

  mTextures[bricksTex->Name] = std::move(bricksTex);
  mTextures[stoneTex->Name] = std::move(stoneTex);
  mTextures[tileTex->Name] = std::move(tileTex);
  mTextures[crateTex->Name] = std::move(crateTex);
  mTextures[iceTex->Name] = std::move(iceTex);
  mTextures[grassTex->Name] = std::move(grassTex);
  mTextures[defaultTex->Name] = std::move(defaultTex);
}
void DxGraphics::Instanced_BuildRootSignature() {
  CD3DX12_DESCRIPTOR_RANGE texTable;
  texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 7, 0, 0);

  // Root parameter can be a table, root descriptor or root constants.
  CD3DX12_ROOT_PARAMETER slotRootParameter[4];

  // Perfomance TIP: Order from most frequent to least frequent.
  slotRootParameter[0].InitAsShaderResourceView(0, 1);
  slotRootParameter[1].InitAsShaderResourceView(1, 1);
  slotRootParameter[2].InitAsConstantBufferView(0);
  slotRootParameter[3].InitAsDescriptorTable(
      1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL);

  auto staticSamplers = GetStaticSamplers();

  // A root signature is an array of root parameters.
  CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4, slotRootParameter,
      (UINT)staticSamplers.size(), staticSamplers.data(),
      D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  // create a root signature with a single slot which points to a descriptor
  // range consisting of a single constant buffer
  ComPtr<ID3DBlob> serializedRootSig = nullptr;
  ComPtr<ID3DBlob> errorBlob = nullptr;
  HRESULT hr =
      D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
          serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

  if (errorBlob != nullptr) {
    ::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
  }
  ThrowIfFailed(hr);

  ThrowIfFailed(device_->CreateRootSignature(0,
      serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(),
      IID_PPV_ARGS(root_signature_.GetAddressOf())));
}
void DxGraphics::Instanced_BuildDescriptorHeaps() {

  //
  // Create the SRV heap.
  //
  D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
  srvHeapDesc.NumDescriptors = 7;
  srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
      &srvHeapDesc, IID_PPV_ARGS(&srv_descriptor_heap_)));

  //
  // Fill out the heap with actual descriptors.
  //
  CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(
      srv_descriptor_heap_->GetCPUDescriptorHandleForHeapStart());

  auto bricksTex = mTextures["bricksTex"]->Resource;
  auto stoneTex = mTextures["stoneTex"]->Resource;
  auto tileTex = mTextures["tileTex"]->Resource;
  auto crateTex = mTextures["crateTex"]->Resource;
  auto iceTex = mTextures["iceTex"]->Resource;
  auto grassTex = mTextures["grassTex"]->Resource;
  auto defaultTex = mTextures["defaultTex"]->Resource;

  D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  srvDesc.Format = bricksTex->GetDesc().Format;
  srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  srvDesc.Texture2D.MostDetailedMip = 0;
  srvDesc.Texture2D.MipLevels = bricksTex->GetDesc().MipLevels;
  srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;
  md3dDevice->CreateShaderResourceView(bricksTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, mCbvSrvDescriptorSize);

  srvDesc.Format = stoneTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = stoneTex->GetDesc().MipLevels;
  md3dDevice->CreateShaderResourceView(stoneTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, mCbvSrvDescriptorSize);

  srvDesc.Format = tileTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = tileTex->GetDesc().MipLevels;
  md3dDevice->CreateShaderResourceView(tileTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, mCbvSrvDescriptorSize);

  srvDesc.Format = crateTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = crateTex->GetDesc().MipLevels;
  md3dDevice->CreateShaderResourceView(crateTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, mCbvSrvDescriptorSize);

  srvDesc.Format = iceTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = iceTex->GetDesc().MipLevels;
  md3dDevice->CreateShaderResourceView(iceTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, mCbvSrvDescriptorSize);

  srvDesc.Format = grassTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = grassTex->GetDesc().MipLevels;
  md3dDevice->CreateShaderResourceView(grassTex.Get(), &srvDesc, hDescriptor);

  // next descriptor
  hDescriptor.Offset(1, mCbvSrvDescriptorSize);

  srvDesc.Format = defaultTex->GetDesc().Format;
  srvDesc.Texture2D.MipLevels = defaultTex->GetDesc().MipLevels;
  md3dDevice->CreateShaderResourceView(defaultTex.Get(), &srvDesc, hDescriptor);
}

void DxGraphics::Instanced_BuildShadersAndInputLayout() {
  const D3D_SHADER_MACRO alphaTestDefines[] = {"ALPHA_TEST", "1", NULL, NULL};

  mShaders["standardVS"] = d3dUtil::CompileShader(
      "Shaders\\Default_Instanced.hlsl", nullptr, "VS", "vs_5_1");
  mShaders["opaquePS"] = d3dUtil::CompileShader(
      "Shaders\\Default_Instanced.hlsl", nullptr, "PS", "ps_5_1");

  mInputLayout = {
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
      {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24,
          D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
  };
}
void DxGraphics::Instanced_BuildMaterials() {
  auto bricks0 = std::make_unique<Material>();
  bricks0->Name = "bricks0";
  bricks0->MatCBIndex = 0;
  bricks0->DiffuseSrvHeapIndex = 0;
  bricks0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  bricks0->FresnelR0 = XMFLOAT3(0.02f, 0.02f, 0.02f);
  bricks0->Roughness = 0.1f;

  auto stone0 = std::make_unique<Material>();
  stone0->Name = "stone0";
  stone0->MatCBIndex = 1;
  stone0->DiffuseSrvHeapIndex = 1;
  stone0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  stone0->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  stone0->Roughness = 0.3f;

  auto tile0 = std::make_unique<Material>();
  tile0->Name = "tile0";
  tile0->MatCBIndex = 2;
  tile0->DiffuseSrvHeapIndex = 2;
  tile0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  tile0->FresnelR0 = XMFLOAT3(0.02f, 0.02f, 0.02f);
  tile0->Roughness = 0.3f;

  auto crate0 = std::make_unique<Material>();
  crate0->Name = "checkboard0";
  crate0->MatCBIndex = 3;
  crate0->DiffuseSrvHeapIndex = 3;
  crate0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  crate0->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  crate0->Roughness = 0.2f;

  auto ice0 = std::make_unique<Material>();
  ice0->Name = "ice0";
  ice0->MatCBIndex = 4;
  ice0->DiffuseSrvHeapIndex = 4;
  ice0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  ice0->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
  ice0->Roughness = 0.0f;

  auto grass0 = std::make_unique<Material>();
  grass0->Name = "grass0";
  grass0->MatCBIndex = 5;
  grass0->DiffuseSrvHeapIndex = 5;
  grass0->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  grass0->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  grass0->Roughness = 0.2f;

  auto skullMat = std::make_unique<Material>();
  skullMat->Name = "skullMat";
  skullMat->MatCBIndex = 6;
  skullMat->DiffuseSrvHeapIndex = 6;
  skullMat->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
  skullMat->FresnelR0 = XMFLOAT3(0.05f, 0.05f, 0.05f);
  skullMat->Roughness = 0.5f;

  mMaterials["bricks0"] = std::move(bricks0);
  mMaterials["stone0"] = std::move(stone0);
  mMaterials["tile0"] = std::move(tile0);
  mMaterials["crate0"] = std::move(crate0);
  mMaterials["ice0"] = std::move(ice0);
  mMaterials["grass0"] = std::move(grass0);
  mMaterials["skullMat"] = std::move(skullMat);
}
void DxGraphics::Instanced_BuildRenderItems() {
  auto skullRitem = std::make_unique<RenderItem>();
  skullRitem->World = MathHelper::Identity4x4();
  skullRitem->TexTransform = MathHelper::Identity4x4();
  skullRitem->ObjCBIndex = 0;
  skullRitem->Mat = mMaterials["tile0"].get();
  skullRitem->Geo = mGeometries["skullGeo"].get();
  skullRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
  skullRitem->InstanceCount = 0;
  skullRitem->IndexCount = skullRitem->Geo->DrawArgs["skull"].IndexCount;
  skullRitem->StartIndexLocation =
      skullRitem->Geo->DrawArgs["skull"].StartIndexLocation;
  skullRitem->BaseVertexLocation =
      skullRitem->Geo->DrawArgs["skull"].BaseVertexLocation;
  skullRitem->Bounds = skullRitem->Geo->DrawArgs["skull"].Bounds;

  // Generate instance data.
  const int n = 5;
  skullRitem->Instances.resize(n * n * n);

  float width = 200.0f;
  float height = 200.0f;
  float depth = 200.0f;

  float x = -0.5f * width;
  float y = -0.5f * height;
  float z = -0.5f * depth;
  float dx = width / (n - 1);
  float dy = height / (n - 1);
  float dz = depth / (n - 1);
  for (int k = 0; k < n; ++k) {
    for (int i = 0; i < n; ++i) {
      for (int j = 0; j < n; ++j) {
        int index = k * n * n + i * n + j;
        // Position instanced along a 3D grid.
        skullRitem->Instances[index].World =
            XMFLOAT4X4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
                0.0f, 1.0f, 0.0f, x + j * dx, y + i * dy, z + k * dz, 1.0f);

        XMStoreFloat4x4(&skullRitem->Instances[index].TexTransform,
            XMMatrixScaling(2.0f, 2.0f, 1.0f));
        skullRitem->Instances[index].MaterialIndex = index % mMaterials.size();
      }
    }
  }

  mAllRitems.push_back(std::move(skullRitem));

  // All the render items are opaque.
  for (auto& e : mAllRitems) {
    mOpaqueRitems.push_back(e.get());
  }
}
void DxGraphics::Instanced_BuildFrameResources() {
  FrameResourceDesc desc = {};
  desc.PassCount = 1;
  desc.ObjectCount = static_cast<UINT>(items_.size());
  desc.MaterialCount = static_cast<UINT>(materials_.size());

  for (int i = 0; i < FRAME_RESOURCE_COUNT; i++) {
    frame_resources_.push_back(
        std::make_unique<FrameResource>(device_.Get(), &desc));
  }
}
void DxGraphics::Instanced_BuildPSOs() {
  D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;

  //
  // PSO for opaque objects.
  //
  ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
  opaquePsoDesc.InputLayout = {mInputLayout.data(), (UINT)mInputLayout.size()};
  opaquePsoDesc.pRootSignature = mRootSignature.Get();
  opaquePsoDesc.VS = {
      reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()),
      mShaders["standardVS"]->GetBufferSize()};
  opaquePsoDesc.PS = {
      reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
      mShaders["opaquePS"]->GetBufferSize()};
  opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
  opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
  opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
  opaquePsoDesc.SampleMask = UINT_MAX;
  opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
  opaquePsoDesc.NumRenderTargets = 1;
  opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
  opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
  opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
  opaquePsoDesc.DSVFormat = mDepthStencilFormat;
  ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(
      &opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));
}
void DxGraphics::Instanced_UpdateObjectCBs() {}
void DxGraphics::Instanced_UpdateMaterialBuffer() {
  auto currMaterialBuffer = current_frame_resource_->MaterialBuffer.get();
  for (auto& e : mMaterials) {
    // Only update the cbuffer data if the constants have changed.  If the
    // cbuffer data changes, it needs to be updated for each FrameResource.
    Material* mat = e.second.get();
    if (mat->NumFramesDirty > 0) {
      XMMATRIX matTransform = XMLoadFloat4x4(&mat->MatTransform);

      MaterialData matData;
      matData.DiffuseAlbedo = mat->DiffuseAlbedo;
      matData.FresnelR0 = mat->FresnelR0;
      matData.Roughness = mat->Roughness;
      XMStoreFloat4x4(&matData.MatTransform, XMMatrixTranspose(matTransform));
      matData.DiffuseMapIndex = mat->DiffuseSrvHeapIndex;

      currMaterialBuffer->CopyData(mat->MatCBIndex, matData);

      // Next FrameResource need to be updated too.
      mat->NumFramesDirty--;
    }
  }
}
void DxGraphics::Instanced_DrawRenderItems(ID3D12GraphicsCommandList* cmdList,
    const std::vector<RenderItem*>& ritems) {

  // For each render item...
  for (size_t i = 0; i < ritems.size(); ++i) {
    auto ri = ritems[i];

    auto vertex_view = ri->Geo->VertexBufferView();
    auto index_view = ri->Geo->IndexBufferView();
    cmdList->IASetVertexBuffers(0, 1, &vertex_view);
    cmdList->IASetIndexBuffer(&index_view);
    cmdList->IASetPrimitiveTopology(ri->PrimitiveType);

    // Set the instance buffer to use for this render-item.  For structured
    // buffers, we can bypass the heap and set as a root descriptor.
    auto instanceBuffer = mCurrFrameResource->InstanceBuffer->Resource();
    mCommandList->SetGraphicsRootShaderResourceView(
        0, instanceBuffer->GetGPUVirtualAddress());

    cmdList->DrawIndexedInstanced(ri->IndexCount, ri->InstanceCount,
        ri->StartIndexLocation, ri->BaseVertexLocation, 0);
  }
}
void DxGraphics::Instanced_BuildShapeGeometry() {}

void DxGraphics::Instanced_BuildSkullGeometry() {
  std::ifstream fin("Models/skull.txt");

  if (!fin) {
    MessageBox(0, "Models/skull.txt not found.", 0, 0);
    return;
  }

  UINT vcount = 0;
  UINT tcount = 0;
  std::string ignore;

  fin >> ignore >> vcount;
  fin >> ignore >> tcount;
  fin >> ignore >> ignore >> ignore >> ignore;

  XMFLOAT3 vMinf3(
      +MathHelper::Infinity, +MathHelper::Infinity, +MathHelper::Infinity);
  XMFLOAT3 vMaxf3(
      -MathHelper::Infinity, -MathHelper::Infinity, -MathHelper::Infinity);

  XMVECTOR vMin = XMLoadFloat3(&vMinf3);
  XMVECTOR vMax = XMLoadFloat3(&vMaxf3);

  std::vector<Vertex> vertices(vcount);
  for (UINT i = 0; i < vcount; ++i) {
    fin >> vertices[i].Pos.x >> vertices[i].Pos.y >> vertices[i].Pos.z;
    fin >> vertices[i].Normal.x >> vertices[i].Normal.y >> vertices[i].Normal.z;

    XMVECTOR P = XMLoadFloat3(&vertices[i].Pos);

    // Project point onto unit sphere and generate spherical texture
    // coordinates.
    XMFLOAT3 spherePos;
    XMStoreFloat3(&spherePos, XMVector3Normalize(P));

    float theta = atan2f(spherePos.z, spherePos.x);

    // Put in [0, 2pi].
    if (theta < 0.0f)
      theta += XM_2PI;

    float phi = acosf(spherePos.y);

    float u = theta / (2.0f * XM_PI);
    float v = phi / XM_PI;

    vertices[i].TexC = {u, v};

    vMin = XMVectorMin(vMin, P);
    vMax = XMVectorMax(vMax, P);
  }

  BoundingBox bounds;
  XMStoreFloat3(&bounds.Center, 0.5f * (vMin + vMax));
  XMStoreFloat3(&bounds.Extents, 0.5f * (vMax - vMin));

  fin >> ignore;
  fin >> ignore;
  fin >> ignore;

  std::vector<std::int32_t> indices(3 * tcount);
  for (UINT i = 0; i < tcount; ++i) {
    fin >> indices[i * 3 + 0] >> indices[i * 3 + 1] >> indices[i * 3 + 2];
  }

  fin.close();

  //
  // Pack the indices of all the meshes into one index buffer.
  //

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);

  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::int32_t);

  auto geo = std::make_unique<MeshGeometry>();
  geo->Name = "skullGeo";

  ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU =
      CreateDefaultBuffer(indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R32_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SubmeshGeometry submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;
  submesh.Bounds = bounds;

  geo->DrawArgs["skull"] = submesh;

  mGeometries[geo->Name] = std::move(geo);
}

void DxGraphics::Instanced_UpdateInstanceData() {
  XMMATRIX view = mCamera.GetView();
  auto det = XMMatrixDeterminant(view);
  XMMATRIX invView = XMMatrixInverse(&det, view);

  auto currInstanceBuffer = current_frame_resource_->InstanceBuffer.get();
  for (auto& e : mAllRitems) {
    const auto& instanceData = e->Instances;

    int visibleInstanceCount = 0;

    for (UINT i = 0; i < (UINT)instanceData.size(); ++i) {
      XMMATRIX world = XMLoadFloat4x4(&instanceData[i].World);
      XMMATRIX texTransform = XMLoadFloat4x4(&instanceData[i].TexTransform);

      det = XMMatrixDeterminant(world);
      XMMATRIX invWorld = XMMatrixInverse(&det, world);

      // View space to the object's local space.
      XMMATRIX viewToLocal = XMMatrixMultiply(invView, invWorld);

      // Transform the camera frustum from view space to the object's local
      // space.
      BoundingFrustum localSpaceFrustum;
      mCamFrustum.Transform(localSpaceFrustum, viewToLocal);

      // Perform the box/frustum intersection test in local space.
      if ((localSpaceFrustum.Contains(e->Bounds) != DirectX::DISJOINT) ||
          (mFrustumCullingEnabled == false)) {
        InstanceData data;
        XMStoreFloat4x4(&data.World, XMMatrixTranspose(world));
        XMStoreFloat4x4(&data.TexTransform, XMMatrixTranspose(texTransform));
        data.MaterialIndex = instanceData[i].MaterialIndex;

        // Write the instance data to structured buffer for the visible objects.
        currInstanceBuffer->CopyData(visibleInstanceCount++, data);
      }
    }

    e->InstanceCount = visibleInstanceCount;

    std::ostringstream outs;
    outs.precision(6);
    outs << "DxEngine " << e->InstanceCount << "/" << e->Instances.size()
         << " Objects";
    mMainWndCaption = outs.str();
  }
}

} // namespace ifire
